package com.yang.mybatis.execute;

import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.execute.request.MybatisPreparedStatementBuilderRequest;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DefaultMybatisPreparedStatementBuilder implements IMybatisPreparedStatementBuilder {
    private Map<String, String> baseAliasMap = new HashMap<>();

    {
        baseAliasMap.put("int", Integer.class.getName());
        baseAliasMap.put("float", Float.class.getName());
        baseAliasMap.put("double", Double.class.getName());
        baseAliasMap.put("long", Long.class.getName());
        baseAliasMap.put("byte", Byte.class.getName());
        baseAliasMap.put("short", Short.class.getName());
        baseAliasMap.put("string", String.class.getName());
        baseAliasMap.put("String", String.class.getName());
        baseAliasMap.put("char", Character.class.getName());
    }
    @Override
    public PreparedStatement buildPreparedStatement(MybatisPreparedStatementBuilderRequest mybatisPreparedStatementBuilderRequest) throws SQLException {
        Connection connection = mybatisPreparedStatementBuilderRequest.getConnection();
        Object[] parameters = mybatisPreparedStatementBuilderRequest.getParameters();

        MybatisSqlStatement mybatisSqlStatement = mybatisPreparedStatementBuilderRequest.getMybatisSqlStatement();
        String rawSql = mybatisSqlStatement.getSql();

        List<String> parameterNameList = new ArrayList<>();
        String sql = extractRawSql(rawSql, parameterNameList);
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        String parameterType = mybatisSqlStatement.getParameterType();
        if (StringUtils.isEmpty(parameterType)) {
            if (parameterNameList.size() != parameters.length) {
                throw new RuntimeException("SQL语句参数个数不匹配====");
            }
            int index = 1;
            for (Object o : parameters) {
                preparedStatement.setObject(index ++, o);
            }
            return preparedStatement;
        }

        try {
            Object parameter = parameters[0];
            if (baseAliasMap.containsKey(parameterType)) {
                if (parameters.length != 1) {
                    throw new RuntimeException("SQL语句有误, 只能有一个parameterType参数");
                }
                preparedStatement.setObject(1, parameter);
                return preparedStatement;
            }
            Class<?> aClass = Class.forName(parameterType);
            Field[] fields = aClass.getDeclaredFields();
            Map<String, Field> filedName2FieldMap = new HashMap<>();
            for (Field field : fields) {
                filedName2FieldMap.put(field.getName(), field);
            }

            List<Object> parameterValueList = new ArrayList<>();
            for (String parameterName : parameterNameList) {
                Field field = filedName2FieldMap.get(parameterName);
                if (field == null) {
                    throw new RuntimeException("SQL参数不存在");
                }
                field.setAccessible(true);
                Object value = field.get(parameter);
                parameterValueList.add(value);
            }

            int index = 1;
            for (Object o : parameterValueList) {
                preparedStatement.setObject(index++, o);
            }
            return preparedStatement;
        } catch (ClassNotFoundException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private String extractRawSql(String rawSql, List<String> parameterNameList) {
        StringBuilder sqlBuilder = new StringBuilder();
        int start = 0;
        int end = -1;
        while ((end = rawSql.indexOf("#", start)) != -1) {
            sqlBuilder.append(rawSql.substring(start, end - 1))
                    .append(" ? ");
            int parameterStart = end + 2;
            int parameterEnd = rawSql.indexOf("}", parameterStart);
            parameterNameList.add(rawSql.substring(parameterStart, parameterEnd));
            start = parameterEnd + 1;
        }
        sqlBuilder.append(rawSql.substring(start));

        return sqlBuilder.toString();
    }
}
